/******************************************************
SQL parser

(c) 1996 Innobase Oy

Created 11/19/1996 Heikki Tuuri
*******************************************************/

/* Historical note: Innobase executed its first SQL string (CREATE TABLE)
on 1/27/1998 */

#include "pars0pars.h"

#ifdef UNIV_NONINL
#include "pars0pars.ic"
#endif

#include "row0sel.h"
#include "row0ins.h"
#include "row0upd.h"
#include "dict0dict.h"
#include "dict0mem.h"
#include "dict0crea.h"
#include "que0que.h"
#include "pars0grm.h"
#include "pars0opt.h"
#include "data0data.h"
#include "data0type.h"
#include "trx0trx.h"
#include "trx0roll.h"
#include "lock0lock.h"
#include "eval0eval.h"

#ifdef UNIV_SQL_DEBUG
/* If the following is set TRUE, the lexer will print the SQL string
as it tokenizes it */

ibool pars_print_lexed = FALSE;
#endif /* UNIV_SQL_DEBUG */

/* Global variable used while parsing a single procedure or query : the code is
NOT re-entrant */
sym_tab_t *pars_sym_tab_global;

/* Global variables used to denote certain reserved words, used in
constructing the parsing tree */

pars_res_word_t pars_to_char_token = {PARS_TO_CHAR_TOKEN};
pars_res_word_t pars_to_number_token = {PARS_TO_NUMBER_TOKEN};
pars_res_word_t pars_to_binary_token = {PARS_TO_BINARY_TOKEN};
pars_res_word_t pars_binary_to_number_token = {PARS_BINARY_TO_NUMBER_TOKEN};
pars_res_word_t pars_substr_token = {PARS_SUBSTR_TOKEN};
pars_res_word_t pars_replstr_token = {PARS_REPLSTR_TOKEN};
pars_res_word_t pars_concat_token = {PARS_CONCAT_TOKEN};
pars_res_word_t pars_instr_token = {PARS_INSTR_TOKEN};
pars_res_word_t pars_length_token = {PARS_LENGTH_TOKEN};
pars_res_word_t pars_sysdate_token = {PARS_SYSDATE_TOKEN};
pars_res_word_t pars_printf_token = {PARS_PRINTF_TOKEN};
pars_res_word_t pars_assert_token = {PARS_ASSERT_TOKEN};
pars_res_word_t pars_rnd_token = {PARS_RND_TOKEN};
pars_res_word_t pars_rnd_str_token = {PARS_RND_STR_TOKEN};
pars_res_word_t pars_count_token = {PARS_COUNT_TOKEN};
pars_res_word_t pars_sum_token = {PARS_SUM_TOKEN};
pars_res_word_t pars_distinct_token = {PARS_DISTINCT_TOKEN};
pars_res_word_t pars_int_token = {PARS_INT_TOKEN};
pars_res_word_t pars_char_token = {PARS_CHAR_TOKEN};
pars_res_word_t pars_float_token = {PARS_FLOAT_TOKEN};
pars_res_word_t pars_update_token = {PARS_UPDATE_TOKEN};
pars_res_word_t pars_asc_token = {PARS_ASC_TOKEN};
pars_res_word_t pars_desc_token = {PARS_DESC_TOKEN};
pars_res_word_t pars_open_token = {PARS_OPEN_TOKEN};
pars_res_word_t pars_close_token = {PARS_CLOSE_TOKEN};
pars_res_word_t pars_consistent_token = {PARS_CONSISTENT_TOKEN};
pars_res_word_t pars_unique_token = {PARS_UNIQUE_TOKEN};
pars_res_word_t pars_clustered_token = {PARS_CLUSTERED_TOKEN};

/* Global variable used to denote the '*' in SELECT * FROM.. */
#define PARS_STAR_DENOTER 12345678
ulint pars_star_denoter = PARS_STAR_DENOTER;

/*************************************************************************
Determines the class of a function code. */
static ulint pars_func_get_class(
    /*================*/
    /* out: function class: PARS_FUNC_ARITH, ... */
    int func) /* in: function code: '=', PARS_GE_TOKEN, ... */
{
  if ((func == '+') || (func == '-') || (func == '*') || (func == '/'))
  {
    return (PARS_FUNC_ARITH);
  }
  else if ((func == '=') || (func == '<') || (func == '>') || (func == PARS_GE_TOKEN) || (func == PARS_LE_TOKEN) ||
           (func == PARS_NE_TOKEN))
  {
    return (PARS_FUNC_CMP);
  }
  else if ((func == PARS_AND_TOKEN) || (func == PARS_OR_TOKEN) || (func == PARS_NOT_TOKEN))
  {
    return (PARS_FUNC_LOGICAL);
  }
  else if ((func == PARS_COUNT_TOKEN) || (func == PARS_SUM_TOKEN))
  {
    return (PARS_FUNC_AGGREGATE);
  }
  else if ((func == PARS_TO_CHAR_TOKEN) || (func == PARS_TO_NUMBER_TOKEN) || (func == PARS_TO_BINARY_TOKEN) ||
           (func == PARS_BINARY_TO_NUMBER_TOKEN) || (func == PARS_SUBSTR_TOKEN) || (func == PARS_CONCAT_TOKEN) ||
           (func == PARS_LENGTH_TOKEN) || (func == PARS_INSTR_TOKEN) || (func == PARS_SYSDATE_TOKEN) ||
           (func == PARS_NOTFOUND_TOKEN) || (func == PARS_PRINTF_TOKEN) || (func == PARS_ASSERT_TOKEN) ||
           (func == PARS_RND_TOKEN) || (func == PARS_RND_STR_TOKEN) || (func == PARS_REPLSTR_TOKEN))
  {
    return (PARS_FUNC_PREDEFINED);
  }
  else
  {
    return (PARS_FUNC_OTHER);
  }
}

/*************************************************************************
Parses an operator or predefined function expression. */
static func_node_t *pars_func_low(
    /*==========*/
    /* out, own: function node in a query tree */
    int func,        /* in: function token code */
    que_node_t *arg) /* in: first argument in the argument list */
{
  func_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(func_node_t));

  node->common.type = QUE_NODE_FUNC;
  dfield_set_data(&(node->common.val), NULL, 0);
  node->common.val_buf_size = 0;

  node->func = func;

  node->class = pars_func_get_class(func);

  node->args = arg;

  UT_LIST_ADD_LAST(func_node_list, pars_sym_tab_global->func_node_list, node);
  return (node);
}

/*************************************************************************
Parses a function expression. */

func_node_t *pars_func(
    /*======*/
    /* out, own: function node in a query tree */
    que_node_t *res_word, /* in: function name reserved word */
    que_node_t *arg)      /* in: first argument in the argument list */
{
  return (pars_func_low(((pars_res_word_t *)res_word)->code, arg));
}

/*************************************************************************
Parses an operator expression. */

func_node_t *pars_op(
    /*====*/
    /* out, own: function node in a query tree */
    int func,         /* in: operator token code */
    que_node_t *arg1, /* in: first argument */
    que_node_t *arg2) /* in: second argument or NULL for an unary
                      operator */
{
  que_node_list_add_last(NULL, arg1);

  if (arg2)
  {
    que_node_list_add_last(arg1, arg2);
  }

  return (pars_func_low(func, arg1));
}

/*************************************************************************
Parses an ORDER BY clause. Order by a single column only is supported. */

order_node_t *pars_order_by(
    /*==========*/
    /* out, own: order-by node in a query tree */
    sym_node_t *column,   /* in: column name */
    pars_res_word_t *asc) /* in: &pars_asc_token or pars_desc_token */
{
  order_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(order_node_t));

  node->common.type = QUE_NODE_ORDER;

  node->column = column;

  if (asc == &pars_asc_token)
  {
    node->asc = TRUE;
  }
  else
  {
    ut_a(asc == &pars_desc_token);
    node->asc = FALSE;
  }

  return (node);
}

/*************************************************************************
Resolves the data type of a function in an expression. The argument data
types must already be resolved. */
static void pars_resolve_func_data_type(
    /*========================*/
    func_node_t *node) /* in: function node */
{
  que_node_t *arg;
  ulint func;

  ut_a(que_node_get_type(node) == QUE_NODE_FUNC);

  arg = node->args;

  func = node->func;

  if ((func == PARS_SUM_TOKEN) || (func == '+') || (func == '-') || (func == '*') || (func == '/') || (func == '+'))
  {
    /* Inherit the data type from the first argument (which must
    not be the SQL null literal whose type is DATA_ERROR) */

    dtype_copy(que_node_get_data_type(node), que_node_get_data_type(arg));

    ut_a(dtype_get_mtype(que_node_get_data_type(node)) == DATA_INT);
  }
  else if (func == PARS_COUNT_TOKEN)
  {
    ut_a(arg);
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_TO_CHAR_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT);
    dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0, 0);
  }
  else if (func == PARS_TO_BINARY_TOKEN)
  {
    if (dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT)
    {
      dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0, 0);
    }
    else
    {
      dtype_set(que_node_get_data_type(node), DATA_BINARY, 0, 0, 0);
    }
  }
  else if (func == PARS_TO_NUMBER_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_VARCHAR);
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_BINARY_TO_NUMBER_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_VARCHAR);
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_LENGTH_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_VARCHAR);
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_INSTR_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_VARCHAR);
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_SYSDATE_TOKEN)
  {
    ut_a(arg == NULL);
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if ((func == PARS_SUBSTR_TOKEN) || (func == PARS_CONCAT_TOKEN))
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_VARCHAR);
    dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0, 0);
  }
  else if ((func == '>') || (func == '<') || (func == '=') || (func == PARS_GE_TOKEN) || (func == PARS_LE_TOKEN) ||
           (func == PARS_NE_TOKEN) || (func == PARS_AND_TOKEN) || (func == PARS_OR_TOKEN) || (func == PARS_NOT_TOKEN) ||
           (func == PARS_NOTFOUND_TOKEN))
  {
    /* We currently have no iboolean type: use integer type */
    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_RND_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT);

    dtype_set(que_node_get_data_type(node), DATA_INT, 0, 4, 0);
  }
  else if (func == PARS_RND_STR_TOKEN)
  {
    ut_a(dtype_get_mtype(que_node_get_data_type(arg)) == DATA_INT);

    dtype_set(que_node_get_data_type(node), DATA_VARCHAR, DATA_ENGLISH, 0, 0);
  }
  else
  {
    ut_error;
  }
}

/*************************************************************************
Resolves the meaning of variables in an expression and the data types of
functions. It is an error if some identifier cannot be resolved here. */
static void pars_resolve_exp_variables_and_types(
    /*=================================*/
    sel_node_t *select_node, /* in: select node or NULL; if
                             this is not NULL then the variable
                             sym nodes are added to the
                             copy_variables list of select_node */
    que_node_t *exp_node)    /* in: expression */
{
  func_node_t *func_node;
  que_node_t *arg;
  sym_node_t *sym_node;
  sym_node_t *node;

  ut_a(exp_node);

  if (que_node_get_type(exp_node) == QUE_NODE_FUNC)
  {
    func_node = exp_node;

    arg = func_node->args;

    while (arg)
    {
      pars_resolve_exp_variables_and_types(select_node, arg);

      arg = que_node_get_next(arg);
    }

    pars_resolve_func_data_type(func_node);

    return;
  }

  ut_a(que_node_get_type(exp_node) == QUE_NODE_SYMBOL);

  sym_node = exp_node;

  if (sym_node->resolved)
  {
    return;
  }

  /* Not resolved yet: look in the symbol table for a variable
  or a cursor with the same name */

  node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list);

  while (node)
  {
    if (node->resolved && ((node->token_type == SYM_VAR) || (node->token_type == SYM_CURSOR)) && node->name &&
        (sym_node->name_len == node->name_len) && (ut_memcmp(sym_node->name, node->name, node->name_len) == 0))
    {
      /* Found a variable or a cursor declared with
      the same name */

      break;
    }

    node = UT_LIST_GET_NEXT(sym_list, node);
  }

  if (!node)
  {
    fprintf(stderr, "PARSER ERROR: Unresolved identifier %s\n", sym_node->name);
  }

  ut_a(node);

  sym_node->resolved = TRUE;
  sym_node->token_type = SYM_IMPLICIT_VAR;
  sym_node->alias = node;
  sym_node->indirection = node;

  if (select_node)
  {
    UT_LIST_ADD_LAST(col_var_list, select_node->copy_variables, sym_node);
  }

  dfield_set_type(que_node_get_val(sym_node), que_node_get_data_type(node));
}

/*************************************************************************
Resolves the meaning of variables in an expression list. It is an error if
some identifier cannot be resolved here. Resolves also the data types of
functions. */
static void pars_resolve_exp_list_variables_and_types(
    /*======================================*/
    sel_node_t *select_node, /* in: select node or NULL */
    que_node_t *exp_node)    /* in: expression list first node, or
                             NULL */
{
  while (exp_node)
  {
    pars_resolve_exp_variables_and_types(select_node, exp_node);

    exp_node = que_node_get_next(exp_node);
  }
}

/*************************************************************************
Resolves the columns in an expression. */
static void pars_resolve_exp_columns(
    /*=====================*/
    sym_node_t *table_node, /* in: first node in a table list */
    que_node_t *exp_node)   /* in: expression */
{
  func_node_t *func_node;
  que_node_t *arg;
  sym_node_t *sym_node;
  dict_table_t *table;
  sym_node_t *t_node;
  dict_col_t *col;
  ulint n_cols;
  ulint i;

  ut_a(exp_node);

  if (que_node_get_type(exp_node) == QUE_NODE_FUNC)
  {
    func_node = exp_node;

    arg = func_node->args;

    while (arg)
    {
      pars_resolve_exp_columns(table_node, arg);

      arg = que_node_get_next(arg);
    }

    return;
  }

  ut_a(que_node_get_type(exp_node) == QUE_NODE_SYMBOL);

  sym_node = exp_node;

  if (sym_node->resolved)
  {
    return;
  }

  /* Not resolved yet: look in the table list for a column with the
  same name */

  t_node = table_node;

  while (t_node)
  {
    table = t_node->table;

    n_cols = dict_table_get_n_user_cols(table);

    for (i = 0; i < n_cols; i++)
    {
      col = dict_table_get_nth_col(table, i);

      if ((sym_node->name_len == ut_strlen(col->name)) &&
          (0 == ut_memcmp(sym_node->name, col->name, sym_node->name_len)))
      {
        /* Found */
        sym_node->resolved = TRUE;
        sym_node->token_type = SYM_COLUMN;
        sym_node->table = table;
        sym_node->col_no = i;
        sym_node->prefetch_buf = NULL;

        dfield_set_type(&(sym_node->common.val), dict_col_get_type(col));
        return;
      }
    }

    t_node = que_node_get_next(t_node);
  }
}

/*************************************************************************
Resolves the meaning of columns in an expression list. */
static void pars_resolve_exp_list_columns(
    /*==========================*/
    sym_node_t *table_node, /* in: first node in a table list */
    que_node_t *exp_node)   /* in: expression list first node, or
                            NULL */
{
  while (exp_node)
  {
    pars_resolve_exp_columns(table_node, exp_node);

    exp_node = que_node_get_next(exp_node);
  }
}

/*************************************************************************
Retrieves the table definition for a table name id. */
static void pars_retrieve_table_def(
    /*====================*/
    sym_node_t *sym_node) /* in: table node */
{
  const char *table_name;

  ut_a(sym_node);
  ut_a(que_node_get_type(sym_node) == QUE_NODE_SYMBOL);

  sym_node->resolved = TRUE;
  sym_node->token_type = SYM_TABLE;

  table_name = (const char *)sym_node->name;

  sym_node->table = dict_table_get_low(table_name);

  ut_a(sym_node->table);
}

/*************************************************************************
Retrieves the table definitions for a list of table name ids. */
static ulint pars_retrieve_table_list_defs(
    /*==========================*/
    /* out: number of tables */
    sym_node_t *sym_node) /* in: first table node in list */
{
  ulint count = 0;

  if (sym_node == NULL)
  {
    return (count);
  }

  while (sym_node)
  {
    pars_retrieve_table_def(sym_node);

    count++;

    sym_node = que_node_get_next(sym_node);
  }

  return (count);
}

/*************************************************************************
Adds all columns to the select list if the query is SELECT * FROM ... */
static void pars_select_all_columns(
    /*====================*/
    sel_node_t *select_node) /* in: select node already containing
                             the table list */
{
  sym_node_t *col_node;
  sym_node_t *table_node;
  dict_table_t *table;
  dict_col_t *col;
  ulint i;

  select_node->select_list = NULL;

  table_node = select_node->table_list;

  while (table_node)
  {
    table = table_node->table;

    for (i = 0; i < dict_table_get_n_user_cols(table); i++)
    {
      col = dict_table_get_nth_col(table, i);

      col_node = sym_tab_add_id(pars_sym_tab_global, (byte *)col->name, ut_strlen(col->name));
      select_node->select_list = que_node_list_add_last(select_node->select_list, col_node);
    }

    table_node = que_node_get_next(table_node);
  }
}

/*************************************************************************
Parses a select list; creates a query graph node for the whole SELECT
statement. */

sel_node_t *pars_select_list(
    /*=============*/
    /* out, own: select node in a query
    tree */
    que_node_t *select_list, /* in: select list */
    sym_node_t *into_list)   /* in: variables list or NULL */
{
  sel_node_t *node;

  node = sel_node_create(pars_sym_tab_global->heap);

  node->select_list = select_list;
  node->into_list = into_list;

  pars_resolve_exp_list_variables_and_types(NULL, into_list);

  return (node);
}

/*************************************************************************
Checks if the query is an aggregate query, in which case the selct list must
contain only aggregate function items. */
static void pars_check_aggregate(
    /*=================*/
    sel_node_t *select_node) /* in: select node already containing
                             the select list */
{
  que_node_t *exp_node;
  func_node_t *func_node;
  ulint n_nodes = 0;
  ulint n_aggregate_nodes = 0;

  exp_node = select_node->select_list;

  while (exp_node)
  {
    n_nodes++;

    if (que_node_get_type(exp_node) == QUE_NODE_FUNC)
    {
      func_node = exp_node;

      if (func_node->class == PARS_FUNC_AGGREGATE)
      {
        n_aggregate_nodes++;
      }
    }

    exp_node = que_node_get_next(exp_node);
  }

  if (n_aggregate_nodes > 0)
  {
    ut_a(n_nodes == n_aggregate_nodes);

    select_node->is_aggregate = TRUE;
  }
  else
  {
    select_node->is_aggregate = FALSE;
  }
}

/*************************************************************************
Parses a select statement. */

sel_node_t *pars_select_statement(
    /*==================*/
    /* out, own: select node in a query
    tree */
    sel_node_t *select_node,          /* in: select node already containing
                                      the select list */
    sym_node_t *table_list,           /* in: table list */
    que_node_t *search_cond,          /* in: search condition or NULL */
    pars_res_word_t *for_update,      /* in: NULL or &pars_update_token */
    pars_res_word_t *consistent_read, /* in: NULL or
                                             &pars_consistent_token */
    order_node_t *order_by)           /* in: NULL or an order-by node */
{
  select_node->state = SEL_NODE_OPEN;

  select_node->table_list = table_list;
  select_node->n_tables = pars_retrieve_table_list_defs(table_list);

  if (select_node->select_list == &pars_star_denoter)
  {
    /* SELECT * FROM ... */
    pars_select_all_columns(select_node);
  }

  if (select_node->into_list)
  {
    ut_a(que_node_list_get_len(select_node->into_list) == que_node_list_get_len(select_node->select_list));
  }

  UT_LIST_INIT(select_node->copy_variables);

  pars_resolve_exp_list_columns(table_list, select_node->select_list);
  pars_resolve_exp_list_variables_and_types(select_node, select_node->select_list);
  pars_check_aggregate(select_node);

  select_node->search_cond = search_cond;

  if (search_cond)
  {
    pars_resolve_exp_columns(table_list, search_cond);
    pars_resolve_exp_variables_and_types(select_node, search_cond);
  }

  if (for_update)
  {
    ut_a(!consistent_read);
    select_node->set_x_locks = TRUE;
    select_node->row_lock_mode = LOCK_X;
  }
  else
  {
    select_node->set_x_locks = FALSE;
    select_node->row_lock_mode = LOCK_S;
  }

  if (consistent_read)
  {
    select_node->consistent_read = TRUE;
  }
  else
  {
    select_node->consistent_read = FALSE;
    select_node->read_view = NULL;
  }

  select_node->order_by = order_by;

  if (order_by)
  {
    pars_resolve_exp_columns(table_list, order_by->column);
  }

  /* The final value of the following fields depend on the environment
  where the select statement appears: */

  select_node->can_get_updated = FALSE;
  select_node->explicit_cursor = NULL;

  opt_search_plan(select_node);

  return (select_node);
}

/*************************************************************************
Parses a cursor declaration. */

que_node_t *pars_cursor_declaration(
    /*====================*/
    /* out: sym_node */
    sym_node_t *sym_node,    /* in: cursor id node in the symbol
                             table */
    sel_node_t *select_node) /* in: select node */
{
  sym_node->resolved = TRUE;
  sym_node->token_type = SYM_CURSOR;
  sym_node->cursor_def = select_node;

  select_node->state = SEL_NODE_CLOSED;
  select_node->explicit_cursor = sym_node;

  return (sym_node);
}

/*************************************************************************
Parses a delete or update statement start. */

upd_node_t *pars_update_statement_start(
    /*========================*/
    /* out, own: update node in a query
    tree */
    ibool is_delete,                    /* in: TRUE if delete */
    sym_node_t *table_sym,              /* in: table name node */
    col_assign_node_t *col_assign_list) /* in: column assignment list, NULL
                                     if delete */
{
  upd_node_t *node;

  node = upd_node_create(pars_sym_tab_global->heap);

  node->is_delete = is_delete;

  node->table_sym = table_sym;
  node->col_assign_list = col_assign_list;

  return (node);
}

/*************************************************************************
Parses a column assignment in an update. */

col_assign_node_t *pars_column_assignment(
    /*===================*/
    /* out: column assignment node */
    sym_node_t *column, /* in: column to assign */
    que_node_t *exp)    /* in: value to assign */
{
  col_assign_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(col_assign_node_t));
  node->common.type = QUE_NODE_COL_ASSIGNMENT;

  node->col = column;
  node->val = exp;

  return (node);
}

/*************************************************************************
Processes an update node assignment list. */
static void pars_process_assign_list(
    /*=====================*/
    upd_node_t *node) /* in: update node */
{
  col_assign_node_t *col_assign_list;
  sym_node_t *table_sym;
  col_assign_node_t *assign_node;
  upd_field_t *upd_field;
  dict_index_t *clust_index;
  sym_node_t *col_sym;
  ulint changes_ord_field;
  ulint changes_field_size;
  ulint n_assigns;
  ulint i;

  table_sym = node->table_sym;
  col_assign_list = node->col_assign_list;
  clust_index = dict_table_get_first_index(node->table);

  assign_node = col_assign_list;
  n_assigns = 0;

  while (assign_node)
  {
    pars_resolve_exp_columns(table_sym, assign_node->col);
    pars_resolve_exp_columns(table_sym, assign_node->val);
    pars_resolve_exp_variables_and_types(NULL, assign_node->val);

    /* ut_a(dtype_get_mtype(dfield_get_type(
                    que_node_get_val(assign_node->col)))
         == dtype_get_mtype(dfield_get_type(
                    que_node_get_val(assign_node->val)))); */

    /* Add to the update node all the columns found in assignment
    values as columns to copy: therefore, TRUE */

    opt_find_all_cols(TRUE, clust_index, &(node->columns), NULL, assign_node->val);
    n_assigns++;

    assign_node = que_node_get_next(assign_node);
  }

  node->update = upd_create(n_assigns, pars_sym_tab_global->heap);

  assign_node = col_assign_list;

  changes_field_size = UPD_NODE_NO_SIZE_CHANGE;

  for (i = 0; i < n_assigns; i++)
  {
    upd_field = upd_get_nth_field(node->update, i);

    col_sym = assign_node->col;

    upd_field_set_field_no(upd_field, dict_index_get_nth_col_pos(clust_index, col_sym->col_no), clust_index, NULL);
    upd_field->exp = assign_node->val;

    if (!dtype_is_fixed_size(dict_index_get_nth_type(clust_index, upd_field->field_no)))
    {
      changes_field_size = 0;
    }

    assign_node = que_node_get_next(assign_node);
  }

  /* Find out if the update can modify an ordering field in any index */

  changes_ord_field = UPD_NODE_NO_ORD_CHANGE;

  if (row_upd_changes_some_index_ord_field_binary(node->table, node->update))
  {
    changes_ord_field = 0;
  }

  node->cmpl_info = changes_ord_field | changes_field_size;
}

/*************************************************************************
Parses an update or delete statement. */

upd_node_t *pars_update_statement(
    /*==================*/
    /* out, own: update node in a query
    tree */
    upd_node_t *node,        /* in: update node */
    sym_node_t *cursor_sym,  /* in: pointer to a cursor entry in
                             the symbol table or NULL */
    que_node_t *search_cond) /* in: search condition or NULL */
{
  sym_node_t *table_sym;
  sel_node_t *sel_node;
  plan_t *plan;

  table_sym = node->table_sym;

  pars_retrieve_table_def(table_sym);
  node->table = table_sym->table;

  UT_LIST_INIT(node->columns);

  /* Make the single table node into a list of table nodes of length 1 */

  que_node_list_add_last(NULL, table_sym);

  if (cursor_sym)
  {
    pars_resolve_exp_variables_and_types(NULL, cursor_sym);

    sel_node = cursor_sym->alias->cursor_def;

    node->searched_update = FALSE;
  }
  else
  {
    sel_node = pars_select_list(NULL, NULL);

    pars_select_statement(sel_node, table_sym, search_cond, NULL, NULL, NULL);
    node->searched_update = TRUE;
    sel_node->common.parent = node;
  }

  node->select = sel_node;

  ut_a(!node->is_delete || (node->col_assign_list == NULL));
  ut_a(node->is_delete || (node->col_assign_list != NULL));

  if (node->is_delete)
  {
    node->cmpl_info = 0;
  }
  else
  {
    pars_process_assign_list(node);
  }

  if (node->searched_update)
  {
    node->has_clust_rec_x_lock = TRUE;
    sel_node->set_x_locks = TRUE;
    sel_node->row_lock_mode = LOCK_X;
  }
  else
  {
    node->has_clust_rec_x_lock = sel_node->set_x_locks;
  }

  ut_a(sel_node->n_tables == 1);
  ut_a(sel_node->consistent_read == FALSE);
  ut_a(sel_node->order_by == NULL);
  ut_a(sel_node->is_aggregate == FALSE);

  sel_node->can_get_updated = TRUE;

  node->state = UPD_NODE_UPDATE_CLUSTERED;

  plan = sel_node_get_nth_plan(sel_node, 0);

  plan->no_prefetch = TRUE;

  if (!((plan->index)->type & DICT_CLUSTERED))
  {
    plan->must_get_clust = TRUE;

    node->pcur = &(plan->clust_pcur);
  }
  else
  {
    node->pcur = &(plan->pcur);
  }

  if (!node->is_delete && node->searched_update && (node->cmpl_info & UPD_NODE_NO_SIZE_CHANGE) &&
      (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))
  {
    /* The select node can perform the update in-place */

    ut_a(plan->asc);

    node->select_will_do_update = TRUE;
    sel_node->select_will_do_update = TRUE;
    sel_node->latch_mode = BTR_MODIFY_LEAF;
  }

  return (node);
}

/*************************************************************************
Parses an insert statement. */

ins_node_t *pars_insert_statement(
    /*==================*/
    /* out, own: update node in a query
    tree */
    sym_node_t *table_sym,   /* in: table name node */
    que_node_t *values_list, /* in: value expression list or NULL */
    sel_node_t *select)      /* in: select condition or NULL */
{
  ins_node_t *node;
  dtuple_t *row;
  ulint ins_type;

  ut_a(values_list || select);
  ut_a(!values_list || !select);

  if (values_list)
  {
    ins_type = INS_VALUES;
  }
  else
  {
    ins_type = INS_SEARCHED;
  }

  pars_retrieve_table_def(table_sym);

  node = ins_node_create(ins_type, table_sym->table, pars_sym_tab_global->heap);

  row = dtuple_create(pars_sym_tab_global->heap, dict_table_get_n_cols(node->table));

  dict_table_copy_types(row, table_sym->table);

  ins_node_set_new_row(node, row);

  node->select = select;

  if (select)
  {
    select->common.parent = node;

    ut_a(que_node_list_get_len(select->select_list) == dict_table_get_n_user_cols(table_sym->table));
  }

  node->values_list = values_list;

  if (node->values_list)
  {
    pars_resolve_exp_list_variables_and_types(NULL, values_list);

    ut_a(que_node_list_get_len(values_list) == dict_table_get_n_user_cols(table_sym->table));
  }

  return (node);
}

/*************************************************************************
Set the type of a dfield. */
static void pars_set_dfield_type(
    /*=================*/
    dfield_t *dfield,      /* in: dfield */
    pars_res_word_t *type) /* in: pointer to a type token */
{
  if (type == &pars_int_token)
  {
    dtype_set(dfield_get_type(dfield), DATA_INT, 0, 4, 0);
  }
  else if (type == &pars_char_token)
  {
    dtype_set(dfield_get_type(dfield), DATA_VARCHAR, DATA_ENGLISH, 0, 0);
  }
  else
  {
    ut_error;
  }
}

/*************************************************************************
Parses a variable declaration. */

sym_node_t *pars_variable_declaration(
    /*======================*/
    /* out, own: symbol table node of type
    SYM_VAR */
    sym_node_t *node,      /* in: symbol table node allocated for the
                           id of the variable */
    pars_res_word_t *type) /* in: pointer to a type token */
{
  node->resolved = TRUE;
  node->token_type = SYM_VAR;

  node->param_type = PARS_NOT_PARAM;

  pars_set_dfield_type(que_node_get_val(node), type);

  return (node);
}

/*************************************************************************
Parses a procedure parameter declaration. */

sym_node_t *pars_parameter_declaration(
    /*=======================*/
    /* out, own: symbol table node of type
    SYM_VAR */
    sym_node_t *node, /* in: symbol table node allocated for the
                      id of the parameter */
    ulint param_type,
    /* in: PARS_INPUT or PARS_OUTPUT */
    pars_res_word_t *type) /* in: pointer to a type token */
{
  ut_a((param_type == PARS_INPUT) || (param_type == PARS_OUTPUT));

  pars_variable_declaration(node, type);

  node->param_type = param_type;

  return (node);
}

/*************************************************************************
Sets the parent field in a query node list. */
static void pars_set_parent_in_list(
    /*====================*/
    que_node_t *node_list, /* in: first node in a list */
    que_node_t *parent)    /* in: parent value to set in all
                           nodes of the list */
{
  que_common_t *common;

  common = node_list;

  while (common)
  {
    common->parent = parent;

    common = que_node_get_next(common);
  }
}

/*************************************************************************
Parses an elsif element. */

elsif_node_t *pars_elsif_element(
    /*===============*/
    /* out: elsif node */
    que_node_t *cond,      /* in: if-condition */
    que_node_t *stat_list) /* in: statement list */
{
  elsif_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(elsif_node_t));

  node->common.type = QUE_NODE_ELSIF;

  node->cond = cond;

  pars_resolve_exp_variables_and_types(NULL, cond);

  node->stat_list = stat_list;

  return (node);
}

/*************************************************************************
Parses an if-statement. */

if_node_t *pars_if_statement(
    /*==============*/
    /* out: if-statement node */
    que_node_t *cond,      /* in: if-condition */
    que_node_t *stat_list, /* in: statement list */
    que_node_t *else_part) /* in: else-part statement list
                           or elsif element list */
{
  if_node_t *node;
  elsif_node_t *elsif_node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(if_node_t));

  node->common.type = QUE_NODE_IF;

  node->cond = cond;

  pars_resolve_exp_variables_and_types(NULL, cond);

  node->stat_list = stat_list;

  if (else_part && (que_node_get_type(else_part) == QUE_NODE_ELSIF))
  {
    /* There is a list of elsif conditions */

    node->else_part = NULL;
    node->elsif_list = else_part;

    elsif_node = else_part;

    while (elsif_node)
    {
      pars_set_parent_in_list(elsif_node->stat_list, node);

      elsif_node = que_node_get_next(elsif_node);
    }
  }
  else
  {
    node->else_part = else_part;
    node->elsif_list = NULL;

    pars_set_parent_in_list(else_part, node);
  }

  pars_set_parent_in_list(stat_list, node);

  return (node);
}

/*************************************************************************
Parses a while-statement. */

while_node_t *pars_while_statement(
    /*=================*/
    /* out: while-statement node */
    que_node_t *cond,      /* in: while-condition */
    que_node_t *stat_list) /* in: statement list */
{
  while_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(while_node_t));

  node->common.type = QUE_NODE_WHILE;

  node->cond = cond;

  pars_resolve_exp_variables_and_types(NULL, cond);

  node->stat_list = stat_list;

  pars_set_parent_in_list(stat_list, node);

  return (node);
}

/*************************************************************************
Parses a for-loop-statement. */

for_node_t *pars_for_statement(
    /*===============*/
    /* out: for-statement node */
    sym_node_t *loop_var,         /* in: loop variable */
    que_node_t *loop_start_limit, /* in: loop start expression */
    que_node_t *loop_end_limit,   /* in: loop end expression */
    que_node_t *stat_list)        /* in: statement list */
{
  for_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(for_node_t));

  node->common.type = QUE_NODE_FOR;

  pars_resolve_exp_variables_and_types(NULL, loop_var);
  pars_resolve_exp_variables_and_types(NULL, loop_start_limit);
  pars_resolve_exp_variables_and_types(NULL, loop_end_limit);

  node->loop_var = loop_var->indirection;

  ut_a(loop_var->indirection);

  node->loop_start_limit = loop_start_limit;
  node->loop_end_limit = loop_end_limit;

  node->stat_list = stat_list;

  pars_set_parent_in_list(stat_list, node);

  return (node);
}

/*************************************************************************
Parses a return-statement. */

return_node_t *pars_return_statement(void)
/*=======================*/
/* out: return-statement node */
{
  return_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(return_node_t));
  node->common.type = QUE_NODE_RETURN;

  return (node);
}

/*************************************************************************
Parses an assignment statement. */

assign_node_t *pars_assignment_statement(
    /*======================*/
    /* out: assignment statement node */
    sym_node_t *var, /* in: variable to assign */
    que_node_t *val) /* in: value to assign */
{
  assign_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(assign_node_t));
  node->common.type = QUE_NODE_ASSIGNMENT;

  node->var = var;
  node->val = val;

  pars_resolve_exp_variables_and_types(NULL, var);
  pars_resolve_exp_variables_and_types(NULL, val);

  ut_a(dtype_get_mtype(dfield_get_type(que_node_get_val(var))) ==
       dtype_get_mtype(dfield_get_type(que_node_get_val(val))));

  return (node);
}

/*************************************************************************
Parses a procedure call. */

func_node_t *pars_procedure_call(
    /*================*/
    /* out: function node */
    que_node_t *res_word, /* in: procedure name reserved word */
    que_node_t *args)     /* in: argument list */
{
  func_node_t *node;

  node = pars_func(res_word, args);

  pars_resolve_exp_list_variables_and_types(NULL, args);

  return (node);
}

/*************************************************************************
Parses a fetch statement. */

fetch_node_t *pars_fetch_statement(
    /*=================*/
    /* out: fetch statement node */
    sym_node_t *cursor,    /* in: cursor node */
    sym_node_t *into_list) /* in: variables to set */
{
  sym_node_t *cursor_decl;
  fetch_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(fetch_node_t));

  node->common.type = QUE_NODE_FETCH;

  pars_resolve_exp_variables_and_types(NULL, cursor);
  pars_resolve_exp_list_variables_and_types(NULL, into_list);

  node->into_list = into_list;

  cursor_decl = cursor->alias;

  ut_a(cursor_decl->token_type == SYM_CURSOR);

  node->cursor_def = cursor_decl->cursor_def;

  ut_a(que_node_list_get_len(into_list) == que_node_list_get_len(node->cursor_def->select_list));

  return (node);
}

/*************************************************************************
Parses an open or close cursor statement. */

open_node_t *pars_open_statement(
    /*================*/
    /* out: fetch statement node */
    ulint type,         /* in: ROW_SEL_OPEN_CURSOR
                        or ROW_SEL_CLOSE_CURSOR */
    sym_node_t *cursor) /* in: cursor node */
{
  sym_node_t *cursor_decl;
  open_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(open_node_t));

  node->common.type = QUE_NODE_OPEN;

  pars_resolve_exp_variables_and_types(NULL, cursor);

  cursor_decl = cursor->alias;

  ut_a(cursor_decl->token_type == SYM_CURSOR);

  node->op_type = type;
  node->cursor_def = cursor_decl->cursor_def;

  return (node);
}

/*************************************************************************
Parses a row_printf-statement. */

row_printf_node_t *pars_row_printf_statement(
    /*======================*/
    /* out: row_printf-statement node */
    sel_node_t *sel_node) /* in: select node */
{
  row_printf_node_t *node;

  node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(row_printf_node_t));
  node->common.type = QUE_NODE_ROW_PRINTF;

  node->sel_node = sel_node;

  sel_node->common.parent = node;

  return (node);
}

/*************************************************************************
Parses a commit statement. */

commit_node_t *pars_commit_statement(void)
/*=======================*/
{
  return (commit_node_create(pars_sym_tab_global->heap));
}

/*************************************************************************
Parses a rollback statement. */

roll_node_t *pars_rollback_statement(void)
/*=========================*/
{
  return (roll_node_create(pars_sym_tab_global->heap));
}

/*************************************************************************
Parses a column definition at a table creation. */

sym_node_t *pars_column_def(
    /*============*/
    /* out: column sym table node */
    sym_node_t *sym_node,  /* in: column node in the symbol
                           table */
    pars_res_word_t *type) /* in: data type */
{
  pars_set_dfield_type(que_node_get_val(sym_node), type);

  return (sym_node);
}

/*************************************************************************
Parses a table creation operation. */

tab_node_t *pars_create_table(
    /*==============*/
    /* out: table create subgraph */
    sym_node_t *table_sym,   /* in: table name node in the symbol
                             table */
    sym_node_t *column_defs, /* in: list of column names */
    void *not_fit_in_memory) /* in: a non-NULL pointer means that
                           this is a table which in simulations
                           should be simulated as not fitting
                           in memory; thread is put to sleep
                           to simulate disk accesses; NOTE that
                           this flag is not stored to the data
                           dictionary on disk, and the database
                           will forget about non-NULL value if
                           it has to reload the table definition
                           from disk */
{
  dict_table_t *table;
  sym_node_t *column;
  tab_node_t *node;
  dtype_t *dtype;
  ulint n_cols;

  n_cols = que_node_list_get_len(column_defs);

  /* As the InnoDB SQL parser is for internal use only,
  for creating some system tables, this function will only
  create tables in the old (not compact) record format. */
  table = dict_mem_table_create(table_sym->name, 0, n_cols, FALSE);

  if (not_fit_in_memory != NULL)
  {
    table->does_not_fit_in_memory = TRUE;
  }

  column = column_defs;

  while (column)
  {
    dtype = dfield_get_type(que_node_get_val(column));

    dict_mem_table_add_col(table, column->name, dtype->mtype, dtype->prtype, dtype->len, dtype->prec);
    column->resolved = TRUE;
    column->token_type = SYM_COLUMN;

    column = que_node_get_next(column);
  }

  node = tab_create_graph_create(table, pars_sym_tab_global->heap);

  table_sym->resolved = TRUE;
  table_sym->token_type = SYM_TABLE;

  return (node);
}

/*************************************************************************
Parses an index creation operation. */

ind_node_t *pars_create_index(
    /*==============*/
    /* out: index create subgraph */
    pars_res_word_t *unique_def,    /* in: not NULL if a unique index */
    pars_res_word_t *clustered_def, /* in: not NULL if a clustered index */
    sym_node_t *index_sym,          /* in: index name node in the symbol
                                    table */
    sym_node_t *table_sym,          /* in: table name node in the symbol
                                    table */
    sym_node_t *column_list)        /* in: list of column names */
{
  dict_index_t *index;
  sym_node_t *column;
  ind_node_t *node;
  ulint n_fields;
  ulint ind_type;

  n_fields = que_node_list_get_len(column_list);

  ind_type = 0;

  if (unique_def)
  {
    ind_type = ind_type | DICT_UNIQUE;
  }

  if (clustered_def)
  {
    ind_type = ind_type | DICT_CLUSTERED;
  }

  index = dict_mem_index_create(table_sym->name, index_sym->name, 0, ind_type, n_fields);
  column = column_list;

  while (column)
  {
    dict_mem_index_add_field(index, column->name, 0, 0);

    column->resolved = TRUE;
    column->token_type = SYM_COLUMN;

    column = que_node_get_next(column);
  }

  node = ind_create_graph_create(index, pars_sym_tab_global->heap);

  table_sym->resolved = TRUE;
  table_sym->token_type = SYM_TABLE;

  index_sym->resolved = TRUE;
  index_sym->token_type = SYM_TABLE;

  return (node);
}

/*************************************************************************
Parses a procedure definition. */

que_fork_t *pars_procedure_definition(
    /*======================*/
    /* out: query fork node */
    sym_node_t *sym_node,   /* in: procedure id node in the symbol
                            table */
    sym_node_t *param_list, /* in: parameter declaration list */
    que_node_t *stat_list)  /* in: statement list */
{
  proc_node_t *node;
  que_fork_t *fork;
  que_thr_t *thr;
  mem_heap_t *heap;

  heap = pars_sym_tab_global->heap;

  fork = que_fork_create(NULL, NULL, QUE_FORK_PROCEDURE, heap);
  fork->trx = NULL;

  thr = que_thr_create(fork, heap);

  node = mem_heap_alloc(heap, sizeof(proc_node_t));

  node->common.type = QUE_NODE_PROC;
  node->common.parent = thr;

  sym_node->token_type = SYM_PROCEDURE_NAME;
  sym_node->resolved = TRUE;

  node->proc_id = sym_node;
  node->param_list = param_list;
  node->stat_list = stat_list;

  pars_set_parent_in_list(stat_list, node);

  node->sym_tab = pars_sym_tab_global;

  thr->child = node;

  pars_sym_tab_global->query_graph = fork;

  return (fork);
}

/*****************************************************************
Parses a stored procedure call, when this is not within another stored
procedure, that is, the client issues a procedure call directly.
In MySQL/InnoDB, stored InnoDB procedures are invoked via the
parsed procedure tree, not via InnoDB SQL, so this function is not used. */

que_fork_t *pars_stored_procedure_call(
    /*=======================*/
    /* out: query graph */
    sym_node_t *sym_node __attribute__((unused)))
/* in: stored procedure name */
{
  ut_error;
  return (NULL);
}

/*****************************************************************
Retrieves characters to the lexical analyzer. */

void pars_get_lex_chars(
    /*===============*/
    char *buf,    /* in/out: buffer where to copy */
    int *result,  /* out: number of characters copied or EOF */
    int max_size) /* in: maximum number of characters which fit
                  in the buffer */
{
  int len;

  len = pars_sym_tab_global->string_len - pars_sym_tab_global->next_char_pos;
  if (len == 0)
  {
#ifdef YYDEBUG
    /* fputs("SQL string ends\n", stderr); */
#endif
    *result = 0;

    return;
  }

  if (len > max_size)
  {
    len = max_size;
  }

#ifdef UNIV_SQL_DEBUG
  if (pars_print_lexed)
  {
    if (len >= 5)
    {
      len = 5;
    }

    fwrite(pars_sym_tab_global->sql_string + pars_sym_tab_global->next_char_pos, 1, len, stderr);
  }
#endif /* UNIV_SQL_DEBUG */

  ut_memcpy(buf, pars_sym_tab_global->sql_string + pars_sym_tab_global->next_char_pos, len);
  *result = len;

  pars_sym_tab_global->next_char_pos += len;
}

/*****************************************************************
Called by yyparse on error. */

void yyerror(
    /*====*/
    const char *s __attribute__((unused)))
/* in: error message string */
{
  ut_ad(s);

  fputs("PARSER ERROR: Syntax error in SQL string\n", stderr);

  ut_error;
}

/*****************************************************************
Parses an SQL string returning the query graph. */

que_t *pars_sql(
    /*=====*/
    /* out, own: the query graph */
    const char *str) /* in: SQL string */
{
  sym_node_t *sym_node;
  mem_heap_t *heap;
  que_t *graph;

  ut_ad(str);

  heap = mem_heap_create(256);

#ifdef UNIV_SYNC_DEBUG
  /* Currently, the parser is not reentrant: */
  ut_ad(mutex_own(&(dict_sys->mutex)));
#endif /* UNIV_SYNC_DEBUG */
  pars_sym_tab_global = sym_tab_create(heap);

  pars_sym_tab_global->sql_string = mem_heap_strdup(heap, str);
  pars_sym_tab_global->string_len = strlen(str);
  pars_sym_tab_global->next_char_pos = 0;

  yyparse();

  sym_node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list);

  while (sym_node)
  {
    ut_a(sym_node->resolved);

    sym_node = UT_LIST_GET_NEXT(sym_list, sym_node);
  }

  graph = pars_sym_tab_global->query_graph;

  graph->sym_tab = pars_sym_tab_global;

  /* fprintf(stderr, "SQL graph size %lu\n", mem_heap_get_size(heap)); */

  return (graph);
}

/**********************************************************************
Completes a query graph by adding query thread and fork nodes
above it and prepares the graph for running. The fork created is of
type QUE_FORK_MYSQL_INTERFACE. */

que_thr_t *pars_complete_graph_for_exec(
    /*=========================*/
    /* out: query thread node to run */
    que_node_t *node, /* in: root node for an incomplete
                      query graph */
    trx_t *trx,       /* in: transaction handle */
    mem_heap_t *heap) /* in: memory heap from which allocated */
{
  que_fork_t *fork;
  que_thr_t *thr;

  fork = que_fork_create(NULL, NULL, QUE_FORK_MYSQL_INTERFACE, heap);
  fork->trx = trx;

  thr = que_thr_create(fork, heap);

  thr->child = node;

  que_node_set_parent(node, thr);

  trx->graph = NULL;

  return (thr);
}
